package com.cg.utl;

import java.sql.*;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

/**
 * JDBC工具类（使用Druid数据库连接池）
 *
 */
public class JDBCUtil {

	private static final String URL = "jdbc:mysql://localhost:3306/t_student";
	private static final String USERNAME = "root";
	private static final String PASSWORD = "452369";
	private static final String DRIVER = "com.mysql.cj.jdbc.Driver";

	static {
		try {
			Class.forName(DRIVER);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	// 获取工具类实例
	public static JDBCUtil getInstance() {
		return new JDBCUtil();
	}

	// 数据库连接
	private Connection connection;
	// statement对象，执行SQL
	private PreparedStatement preparedStatement;
	// 查询结果集
	private ResultSet resultSet;

	/**
	 * 从数据库连接池中获取连接，因为是内部使用，所以设为private不暴露给外界使用
	 *
	 * @return 从数据库连接池中获取连接
	 */
	private Connection getConnection() {
		connection = null;
		try {
			connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return connection;
	}

	/**
	 * 释放数据库资源
	 */
	public void close() {
		// 释放数据库资源
		try {
			if (resultSet != null) {
				resultSet.close();
			}
			if (preparedStatement != null) {
				preparedStatement.close();
			}
			if (connection != null) {
				connection.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 执行增删改sql语句
	 * 
	 * @param sql
	 *            SQL语句，其中使用?占位符表示参数
	 * @param params
	 *            SQL中需要的参数值，需要按照SQL中?的顺序，将参数值放入List中
	 * @return 是否执行成功
	 */
	public boolean update(String sql, List<Object> params) {

		// 获取连接
		getConnection();
		int rows = 0;
		if (connection != null) {
			try {
				// 获取PreparedStatement对象
				preparedStatement = connection.prepareStatement(sql);
				// 循环设置参数
				for (int i = 0; i < params.size(); i++) {
					preparedStatement.setObject(i + 1, params.get(i));
				}
				// 这一步非常必要，帮助我们查看执行的sql到底是什么样！用于解决问题
				guessPreparedSql(sql, params);
				// 执行SQL语句
				rows = preparedStatement.executeUpdate();

			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return rows > 0;
	}

	/**
	 * 执行select语句
	 * 
	 * @param sql
	 *            SQL语句，其中使用?占位符表示参数
	 * @param params
	 *            SQL中需要的参数值，需要按照SQL中?的顺序，将参数值放入List中
	 * @return 查询结果集
	 */
	public ResultSet query(String sql, List<Object> params) {
		// 获取连接
		getConnection();
		if (connection != null) {
			try {
				// 获取PreparedStatement对象
				preparedStatement = connection.prepareStatement(sql);
				// 循环设置参数
				for (int i = 0; i < params.size(); i++) {
					preparedStatement.setObject(i + 1, params.get(i));
				}
				// 这一步非常必要，帮助我们查看执行的sql到底是什么样！
				guessPreparedSql(sql, params);
				// 执行sql，并获取结果集
				resultSet = preparedStatement.executeQuery();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		// 需要返回ResultSet需要提供给外部使用
		return resultSet;
	}

	/**
	 * 执行无参select语句
	 * 
	 * @param sql
	 *            SQL语句
	 * @return 结果集
	 */
	public ResultSet query(String sql) {
		return query(sql, Collections.emptyList());
	}

	/**
	 * 获得PreparedStatement向数据库提交的SQL语句
	 *
	 * @param sql
	 * @param params
	 * @return
	 */
	private String guessPreparedSql(String sql, List<Object> params) {
		int paramNum = 0;

		// 如果参数集不为空，取得其长度
		if (null != params) {
			paramNum = params.size();
		}

		// 如果没有参数，说明不是动态SQL语句，直接返回原sql
		if (1 > paramNum) {
			System.out.println(LocalDateTime.now() + "\tprepared sql: " + sql);
			return sql;
		}

		// 如果有参数，则是动态SQL语句，需要构造并返回新sql
		StringBuffer returnSQL = new StringBuffer();
		String[] subSQL = sql.split("\\?");

		// 开始循环替换问号为参数值
		for (int i = 0; i < paramNum; i++) {
			Object value = params.get(i);
			if (value == null) {
				System.err.printf("第 %d 个参数的值为 null %n", i + 1);
				returnSQL.append(subSQL[i]).append(value);
				continue;
			}
			if (value instanceof Number) {
				// 数值不需要引号
				returnSQL.append(subSQL[i]).append(value);
			} else {
				// 非数值需要引号，遇到特殊字符将其转义输出
				String str = value.toString().replaceAll("(['\\\\])", "\\\\$1");
				returnSQL.append(subSQL[i]).append(" '").append(str).append("' ");
			}
		}

		// 如果问号不是原sql的最后一个字符，则将改问号后的部分添加到returnSQL中
		if (subSQL.length > params.size()) {
			returnSQL.append(subSQL[subSQL.length - 1]);
		}

		String formatSql = returnSQL.toString();
		System.out.println(LocalDateTime.now() + "\tprepared sql: " + formatSql);

		return formatSql;
	}
}
